﻿using Microsoft.Extensions.Caching;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Daemon.Data.Infrastructure.Util
{
	public interface IMemoryCacheUtil
	{
		bool ContainsKey(object key);

		object GetCache(object key);

		T GetOrAddCache<T>(string key, MemoryCacheEntryOptions options, Func<T> getCacheData);

		void AddCacheWithPolicy(string key, object value, int expirationTime, Action removedCallback);

		Task<T> GetOrAddCacheAsync<T>(string key, MemoryCacheEntryOptions options, Func<Task<T>> getCacheData);

		void AddCache(string key, object value, int expirationTime);

		void RemoveCache(object key);

		void SetCache(object key, object value, MemoryCacheEntryOptions options = null);
	}

	/// <summary>
	/// This Util is used for general web api and vendor area web api.
	/// Every changes need confirm all web api are correct.
	/// </summary>
	public class MemoryCacheUtil : IMemoryCacheUtil
	{
		private MemoryCache _cache = new MemoryCache(new MemoryCacheOptions() { });

		public bool ContainsKey(object key)
		{
			return _cache.TryGetValue(key, out object _);
		}

		public object GetCache(object key)
		{
			return _cache.Get(key);
		}

		public T GetOrAddCache<T>(string key, MemoryCacheEntryOptions options, Func<T> getCacheData)
		{
			T data;
			var cachedData = GetCache(key);

			if (cachedData == null)
			{
				data = getCacheData();
				if (data != null)
				{
					SetCache(key, data, options);
				}
			}
			else
			{
				data = (T)cachedData;
			}

			return data;
		}

		public async Task<T> GetOrAddCacheAsync<T>(string key, MemoryCacheEntryOptions options, Func<Task<T>> getCacheData)
		{
			T data;
			var cachedData = GetCache(key);

			if (cachedData == null)
			{
				data = await getCacheData();
				if (data != null)
				{
					SetCache(key, data, options);
				}
			}
			else
			{
				data = (T)cachedData;
			}

			return data;
		}

		public void AddCacheWithPolicy(string key, object value, int expirationTime, Action removedCallback)
		{
			MemoryCacheEntryOptions cacheEntityOps = new MemoryCacheEntryOptions();
			cacheEntityOps.RegisterPostEvictionCallback((key, value, reason, state) =>
			{
				removedCallback();
			});

			cacheEntityOps.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(expirationTime);

			_cache.Set(key, value, cacheEntityOps);
		}

		public void AddCache(string key, object value, int expirationTime)
		{
			_cache.Set(key, value, DateTime.Now.AddSeconds(expirationTime));
		}

		public void SetCache(object key, object value, MemoryCacheEntryOptions options = null)
		{
			_cache.Set(key, value, options);
		}

		public void RemoveCache(object key)
		{
			_cache.Remove(key);
		}
	}
}
